import base64
import random
from warnings import catch_warnings
import jwt
import time

from django.core.mail import send_mail
from django.template.loader import render_to_string

from Small_BackEnd import settings
from app.jwtconf import *
from app.models import Emails, Users
from app.statusenum import EmailSendStatusEnum
try:
    # import required by mypy to perform type checking, not used for normal execution
    from typing import Callable, Dict, List, Optional, Union # NOQA
except ImportError:
    pass


import hmac
import hashlib
def sha256_sign(salt:str ,payloadstr:str):
    """
    sha256加密
    :param str: 字符串
    :return: 加密结果
    """
    return hmac.new(salt.encode('utf-8'), payloadstr.encode('utf-8'), hashlib.sha256).hexdigest()
    
def sha256_sign_weak(salt:str ,payloadstr:str):
    """
    sha256加密
    :param str: 字符串
    :return: 加密结果
    """
    return hmac.new(salt.encode('utf-8'), payloadstr.encode('utf-8'), hashlib.sha256).hexdigest()[:20]
    

def create_token(payload, 
                tokentype=None, # type: Optional[str]
                exp=None):
    """
    创建 JWT Token
    :param user: 用户对象
    :param exp: 过期时间 (秒) 默认值由ACCESS_TOKEN_LIFETIME.total_seconds()计算
    :return: JWT Token
    """
    salt = SECRET_KEY
    if exp is None:
        exp = ACCESS_TOKEN_LIFETIME.total_seconds()
    header = {
        "typ": "jwt",
        "alg": "HS256"
    }
    payload = {
        "payload": payload,
        "tokentype": tokentype,
        "exp": int(time.time()) + exp,
        "iat": int(time.time())
    }
    token = jwt.encode(payload, salt, algorithm='HS256')
    if token.__class__ == bytes:
        token = token.decode('utf-8')
    elif token.__class__ == str:
        pass
    return token


def create_file_token(payload:str):
    """
    创建文件 Token
    :param payload: 数据
    :return: Token
    """

    # 生成签名
    salt = SECRET_KEY
    token = sha256_sign_weak(salt, payload)
    #对payload做BASE64编码（urlsafe_b64encode）
    payload = base64.urlsafe_b64encode(payload.encode('utf-8')).decode('utf-8')

    return payload + '.' + token
def decode_file_token(token):
    """
    解码文件 Token
    :param token: 文件 Token
    :return: 文件内容
    """
    try:
        # 对token进行解码
        token = token.split('.')
        if len(token) != 2:
            return None
        payload = token[0]
        signature = token[1]
        # 对payload做BASE64解码（urlsafe_b64decode）
        payload = base64.urlsafe_b64decode(payload.encode('utf-8')).decode('utf-8')
        # 对签名做校验
        salt = SECRET_KEY
        if sha256_sign_weak(salt, payload) != signature:
            return None
        return payload
    except Exception as e:
        return None



def validate_token(token,
                   tokentype=None # type: Optional[str]
                   ):
    """
    验证 JWT Token
    :param token: JWT Token
    :return: 认证信息 (失败返回 None)
    """
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
    except jwt.ExpiredSignatureError:
        return None
    except jwt.InvalidTokenError:
        return None
    if tokentype is not None and payload.get('tokentype') != tokentype:
        return None
    return payload


def send_verification_code(email, send_type, link=None, user_name=None, team_name=None):
    """
    发送邮箱验证码
    :param email: 邮箱地址
    :param send_type: 发送类型
    :param link: 邮件内容-链接
    :param user_name: 用户id
    :param team_name: 团队id
    :return: 邮件发送状态
    """
    from_email = settings.EMAIL_FROM
    to_email = email
    code = random.randint(1000, 9999)
    subject = "InkBook(墨书) 这是一封验证邮件..."  # 标题
    message = {}  # 消息
    send_time = datetime.datetime.now()

    if send_type == '0':
        context = {'email': to_email, 'code': code, 'create_time': send_time}
        html_message = render_to_string('active.html', context)  # html消息
    elif send_type == '1':
        context = {'user_name': user_name, 'forget_url': link}
        html_message = render_to_string('forget.html', context)  # html消息
    elif send_type == '2':
        context = {'user_name': user_name, 'team_name': team_name, 'invite_url': link}
        html_message = render_to_string('invite.html', context)  # html消息
    else:
        return EmailSendStatusEnum.FAIL.value

    # 数据库存档 !!!暂时保留
    new_email = Emails()
    new_email.email_address = email
    new_email.verification_code = code
    new_email.send_time = send_time
    new_email.send_type = send_type
    new_email.save()

    send_status = send_mail(subject, message, from_email, [to_email], html_message=html_message)

    return send_status


def getTokenPayloadOfRequest(request):
    """
    验证请求中的 Token
    :param request: 请求对象
    :return: 验证结果
    """
    token = request.META.get('HTTP_AUTHORIZATION')
    if token is None:
        return None
    payload = validate_token(token, TokenTypeEnum.LOGIN_TOKEN.value)
    
    # just for simplify logic
    if payload is None:
        return None

    return payload

def generate_newname_norepeat(original_name, doc_name_list: list, suffix=''):
    # 用于文档新建的工具函数
    # 在原文件名后加入' {suffix}',若doc_name_list中含相同字符串，则尝试在再后面加入最低的不重复'(%d)'
    new_name = original_name
    if new_name in doc_name_list:
        if suffix == '':
            pass
        else:
            new_name = original_name + ' {0}'.format(suffix)
    if new_name in doc_name_list:
        for i in range(1, 10000):
            new_name = original_name + ' {0}({1})'.format(suffix, i)
            if new_name not in doc_name_list:
                break
            if i == 9999:
                # raise 错误 文件名生成失败 
                raise Exception('文件名生成失败')

    return new_name

